//
//  HtmlOperator.m
//  MarketDataColloctor
//
//  Created by jiro on 10/03/10.
//  Copyright 2010 jiro music. All rights reserved.
//

#import "HtmlOperator.h"
#import "../../Commons.h"

@implementation HtmlOperator

@synthesize errorMessage;

@synthesize saveFolder;

- (void) writeHtmlStringsAsFile: (NSArray*) htmlStrings ofCode0: (NSString*) code0 code1: (NSString*) code1
{
	NSString* html;
	NSEnumerator* e = [ htmlStrings objectEnumerator ];
	int count = 1;
	while ( html = [ e nextObject ] )
	{
		NSString* path;
		if ( count == 1 ) { 
			path = [ NSString stringWithFormat: @"%@/%@-%@.html", saveFolder, code0, code1 ];
		} else {
			path = [ NSString stringWithFormat: @"%@/%@-%@-%04d.html", saveFolder, code0, code1, count ];
		}
		count++;

		NSError* error = nil;
		[ html writeToFile: path atomically: YES encoding:NSUTF8StringEncoding error: & error ];
		if ( error != nil )
		{
			NSString* s = [ NSString stringWithFormat: @"解析に失敗した HTML のファイル保存に失敗した。\n\tcode:%@-%@, path: %@\n\t%@\n",
					code0, code1, path, [ error localizedDescription ] ];
			NSLog( @"%@", s );
			[ errorMessage appendString: s];
		}
	}
}

- (void) updateLogTableWhereCode0: (NSString*) code0 
		 code1: (NSString*) code1 
		 lastDate: (NSDate*) lastDate
		  withDbOperator: (DbOperator*) dbOperator
{
	//	ログを更新
	NSString* sql = [ NSString stringWithFormat: @"update %@ set %@ = '%@' where %@ = '%@' and %@ = '%@' and %@ = %d;",
		[ DataCollectLog tableName ], 
		kText_LatestDate, [ lastDate description ],
		kText_Code0, code0, 
		kText_Code1, code1, 
		kText_MarketDataType, marketDataType ];
	PGresult* res = nil;
	if ( DbResponse_Success != [ dbOperator executeQuery: sql intoResult: & res ] )
	{
		NSLog( @"fail to update log code: %@-%@", code0, code1 );
		[ errorMessage appendFormat: @"ログの更新に失敗:%@\n\t%@\n", [ dbOperator errorMessage ], sql ];
	}
	PQclear( res );
}

- (id) initWithSaveFolder: (NSString*) saveFolder_
{
	self = [ super init ];
	errorMessage = [ [ NSMutableString alloc ] init ];
	saveFolder = saveFolder_;
	return self;
}

- (BOOL) save
{
	DataCollectLogList* logs = [ MarketJaDbOperator allDataCollectLog: marketDataType ];
	NSEnumerator* logEnum = [ logs objectEnumerator ];
	NSFileManager* manager = [ NSFileManager defaultManager ];
	DataCollectLog* log;
	DbOperator* dbOperator = [ DbOperator defaultOperator ];
	while ( log = [ logEnum nextObject ] )
	{	
		NSLog( @"processing code: %@-%@", log.code0, log.code1 );
		
		//	前提チェック
//		double d = [ log.latestDate timeIntervalSinceDate: [ NSDate date ] ];
		if ( [ log.latestDate timeIntervalSinceDate: [ NSDate date ] ] > - 3600 * 24 )
		{
			continue;
		}
		if ( YES == [ manager fileExistsAtPath: [ NSString stringWithFormat: @"%@/%@%@.html", saveFolder, log.code0, log.code1 ] ] )
		{	//	未処理の html がある時は、新たにデータ取得しない。
			continue;
		}

		//	html を収集
		NSMutableArray* htmlStrings = [ NSMutableArray array ]; 
		if ( NO == [ collector
						collectWithCode0: log.code0
						code1: log.code1
						andMarketDataType: marketDataType 
						intoStrings: htmlStrings ] )
		{
			NSLog( @"fail to collect html code: %@-%@", log.code0, log.code1 );
			[ errorMessage appendString: [ collector errorMessage ] ];
			continue;
		}

		//	htmlからデータを抽出
		NSString* html;
		NSEnumerator* htmlEnumerator = [ htmlStrings objectEnumerator ];
		DbTableEntryList* list = [ [ DbTableEntryList alloc ] init ];
		BOOL interpretError = NO;
		while ( html = [ htmlEnumerator nextObject ] )
		{
//			NSLog( @"html\n%@", html );
			DbTableEntryList* tempList =[ interpreter interpret: html ];
			if ( tempList == nil )
			{
				[ errorMessage appendString: [ interpreter errorMessage ] ];
				interpretError = YES;
				break;
			}
			NSEnumerator* tempListEnum = [ tempList objectEnumerator ];
			DbTableEntry* entry;
			while ( entry = [ tempListEnum nextObject ] )
			{
				[ list addObject: entry ];
			}
		}
		if ( interpretError )
		{	//	全HTMLテキストをファイル保存
			NSLog( @"fail to interpert html code: %@-%@", log.code0, log.code0 );
			[ self writeHtmlStringsAsFile: htmlStrings ofCode0: log.code0 code1: log.code1 ];
			continue;
		}

		if ( [ list count ] == 0 )
		{
			[ self updateLogTableWhereCode0: log.code0 code1: log.code1 lastDate: [ NSDate date ] withDbOperator: dbOperator ];
			continue;
		}

		//	DBにデータを挿入
		NSDate* lastDate = nil;
		NSArray* sqls = [ self createInsertSqlStatements: list toDate: log.latestDate lastDate: & lastDate ];
		if ( 0 == [ sqls count ] )
		{
			[ self updateLogTableWhereCode0: log.code0 code1: log.code1 lastDate: [ NSDate date ] withDbOperator: dbOperator ];
			continue;
		}

		NSEnumerator* sqlEnum = [ sqls objectEnumerator ];
		NSString* sql;
		BOOL writeFile = NO;
		while ( sql = [ sqlEnum nextObject ] )
		{
			PGresult* res = nil;
			if ( DbResponse_Success != [ dbOperator executeQuery: sql intoResult: & res ] )
			{
				if ( NO == writeFile ) {
					[ self writeHtmlStringsAsFile: htmlStrings ofCode0: log.code0 code1: log.code1 ];
					writeFile = YES;
				}
				[ errorMessage 
					appendString: [ NSString stringWithFormat: @"SQLの実行に失敗: %@\n\tsql:%@\n", 
						[ dbOperator errorMessage ], sql ] ];
				NSLog( @"%@", errorMessage );
			}
			PQclear( res );
		}

		if ( YES == writeFile )
		{
			NSLog( @"fail to execute sql: %@-%@", log.code0, log.code1 );
			continue;
		}

		//	ログを更新
		[ self updateLogTableWhereCode0: log.code0 code1: log.code1 lastDate: lastDate withDbOperator: dbOperator ];

	}

	NSLog( @"収集終了" );

	[ dbOperator disconnect ];

	return YES;

}

- (NSArray*) createInsertSqlStatements: (DbTableEntryList*) list
	toDate: (NSDate*) latestDate
	lastDate: (NSDate**) lastDate
{
	*lastDate = nil;
	NSEnumerator* e = [ list objectEnumerator ];
	DbExchangeTableEntry* entry = (DbExchangeTableEntry*) [ list objectAtIndex: 0 ];
	NSMutableString* sql =
		[ NSMutableString stringWithFormat: @"insert into %@ values\n  ", [ [ entry class ] tableName ] ];
	BOOL isValueAdded = NO;
	while ( entry = [ e nextObject ] )
	{
		NSLog( @"%@", entry.exchangeDate );
		if ( [ entry.exchangeDate timeIntervalSinceDate: latestDate ] <= 0 )
		{
			continue;
		}

		if ( NO == isValueAdded )
		{
			if ( *lastDate == nil )
			{
				*lastDate = entry.exchangeDate;
			}
			isValueAdded = YES;
			[ sql appendString: [ entry queryInsertValue ] ];
		}
		else
		{
			if ( [ *lastDate timeIntervalSinceDate: entry.exchangeDate ] < 0 )
			{
				*lastDate = entry.exchangeDate;
			}	
			[ sql appendString: @", " ];
			[ sql appendString: [ entry queryInsertValue ] ];
		}

		[ sql appendString: @"\n" ];
	}

	if ( NO == isValueAdded )
	{
		return nil;
	}
	[ sql appendString: @";\n" ];

	NSArray* a = [ NSArray arrayWithObject: sql ];
	return a;
}

@end
